local M = {
  "mason-org/mason.nvim",
  event = "VeryLazy",
  dependencies = {
    {
      "mason-org/mason-lspconfig.nvim",
      dependencies = {
        -- LSP
        "neovim/nvim-lspconfig",
        event = { "BufReadPre", "BufNewFile" },
        -- opts = {
        --   inlay_hints = { enabled = true },
        -- },
        dependencies = {
          "folke/neodev.nvim",

          -- Lsp 的相关插件加载的工作进度
          "j-hui/fidget.nvim",

          -- jsonls 的格式服务工具
          "b0o/schemastore.nvim",

          -- 格式化服务工具
          "stevearc/conform.nvim",
        },
      },
    },
    {
      "antosha417/nvim-lsp-file-operations",
      dependencies = {
        "nvim-lua/plenary.nvim",
        "nvim-tree/nvim-tree.lua",
      },
      config = function()
        -- 提供 提供文件重命名全局修改
        require("lsp-file-operations").setup()
      end,
    },
    {
      -- LSP 的 UI 美化
      -- "glepnir/lspsaga.nvim",
      "nvimdev/lspsaga.nvim",
      enabled = true,
      event = "LspAttach",
      dependencies = {
        "nvim-tree/nvim-web-devicons",
        -- Please make sure you install N and markdown_inline parser
        "nvim-treesitter/nvim-treesitter",
      },
      config = function()
        -- 使用 terminal
        vim.keymap.set({ "n", "v", "t" }, "<C-\\>", "<cmd>Lspsaga term_toggle<cr>", { desc = "Open terminal" })

        require("lspsaga").setup {
          -- ui = {
          --   border = "rounded",
          --   -- code_action = '󰛨' -- 当前行需要 code_action 的提示
          -- },
        }
      end,
    },
  },
  config = function()
    -- Change the Diagnostic symbols in the sign column (gutter)
    local signs = { Error = " ", Warn = " ", Hint = "󰠠 ", Info = " " }
    for type, icon in pairs(signs) do
      local hl = "DiagnosticSign" .. type
      vim.fn.sign_define(hl, { text = icon, texthl = hl, numhl = "" })
    end

    -- neodev 与 fidget 必须在 lsp 配置服务之前设置
    -- lua 全局变量的识别,比如 vim 还有就是和 fidget 集成
    require("neodev").setup {
      library = {
        plugins = { "nvim-dap-ui" },
        types = true,
      },
    }
    -- fidget 必须在 lsp 配置服务之前设置
    require("fidget").setup()

    require("mason").setup {
      install_root_dir = vim.fn.stdpath "data" .. "/mason",
      -- ensure_installed = {
      --   "clangd",
      --   "rust-analyzer",
      --   "gopls",
      -- },
      ui = {
        border = "rounded",
        icons = {
          package_installed = "✓",
          package_pending = "➜",
          package_uninstalled = "✗",
        },
      },
    }
    require("mason-lspconfig").setup {
      automatic_installation = true,

      ensure_installed = {
        -- https://github.com/williamboman/mason-lspconfig.nvim
        -- https://mason-registry.dev/registry/list
        -- 使用服务器别名
        "lua_ls",
        "rust_analyzer",
        "denols",
        "ts_ls", -- typescript
        "eslint",
        "clangd",
        "denols",
        "cssls", -- SCSS
        "html",
        "tailwindcss",
        "svelte",
        "graphql",
        "emmet_ls",
        "pyright",
      },
    }

    -- fomat servers
    -- 注意：区分 lsp 的服务， 和对应的语言代码格式化或代码检测服务的安装方式不一样
    local mason_registry = require "mason-registry"
    local format_servers = {
      "codelldb",
      "clang-format", -- c/c++
      "gofumpt", -- go
      "goimports", -- go
      "stylua", -- lua
      "luacheck",
      "jq", -- json
      "shfmt", -- sh

      "pylint", -- python
      "black", -- python
      "isort", -- python
      "mypy", -- python
      "ruff", -- python
      "pyright",

      -- "biome", -- 速度比 prettier 快的格式化工具
      "prettier", -- js
      "prettierd", -- js
      "eslint_d", -- js ts
      "markdownlint",
    }

    local format_severs = function()
      for _, name in pairs(format_servers) do
        if not mason_registry.is_installed(name) then
          local package = mason_registry.get_package(name)
          vim.notify("[mason-registry]" .. " Installing " .. name)
          package:install()
        end
      end
    end
    mason_registry.refresh(vim.schedule_wrap(format_severs))

    -- 注意顺序 mason -> lspconfig => mason-lspconfig 把 mason 和 lspconfig 结合起来

    -- local capabilities = require("cmp_nvim_lsp").default_capabilities(vim.lsp.protocol.make_client_capabilities())
    local capabilities = vim.lsp.protocol.make_client_capabilities()
    capabilities = vim.tbl_deep_extend("force", capabilities, require("cmp_nvim_lsp").default_capabilities())

    local servers = {
      lua_ls = {
        settings = {
          Lua = {
            telementry = { enabled = false },
            diagnostics = {
              -- lua 中全局的 vim 变量不报警告
              globals = { "vim" },
            },
            workspace = {
              checkThirdParty = false,
              -- Make the server aware of Neovim runtime files
              library = vim.api.nvim_get_runtime_file("", true),
            },
            codeLens = {
              enable = true,
            },
            completion = {
              callSnippet = "Replace",
            },
            doc = {
              privateName = { "^_" },
            },
            hint = {
              enable = true,
              setType = false,
              paramType = true,
              paramName = "Disable",
              semicolon = "Disable",
              arrayIndex = "Disable",
            },
          },
        },
      },
      -- Server-specific settings. See `:help lspconfig-setup`
      -- 可以使用独立的服务
      rust_analyzer = {
        root_dir = require("lspconfig/util").root_pattern { "Cargo.toml", ".rustfmt.toml" },
        filetypes = { "rust" },
        settings = {
          ["rust-analyzer"] = {
            -- enable clippy on save
            checkOnSave = {
              command = "clippy", -- clippy / check
            },
            imports = {
              granularity = {
                group = "module",
              },
              prefix = "self",
            },
            cargo = {
              allFeatures = true,
              buildScripts = {
                enable = true,
              },
            },
            procMacro = {
              enable = true,
              ignored = {
                leptos_macro = {
                  -- optional: --
                  -- "component",
                  "server",
                },
              },
            },
          },
        },
      },

      -- npm install -g typescript typescript-language-server
      ts_ls = {
        cmd = { "typescript-language-server", "--stdio" },
        root_dir = require("lspconfig/util").root_pattern {
          "tsconfig.json",
          "jsconfig.json",
          "package.json",
          ".git",
        },
        filetypes = {
          "javascript",
          "typescript",
          "javascriptreact",
          "javascript.jsx",
          "typescriptreact",
          "typescript.tsx",
          "vue",
        },
        init_options = {
          preferences = {
            disableSuggestions = true,
          },
          plugins = {
            {
              -- npm install -g volar @vue/typescript-plugin
              name = "@vue/typescript-plugin",
              -- location = "/usr/local/lib/node_modules/@vue/typescript-plugin",
              location = "/home/timly/installsoft/node_global/lib/node_modules/@vue/typescript-plugin",
              languages = { "javascript", "typescript", "vue" },
            },
          },
        },
      },
      -- pylsp = { -- pacman -S python-lsp-black
      --   cmd = { "pylsp" },
      --   filetypes = { "python" },
      --   settings = {
      --     pylsp = {
      --       plugins = {
      --         -- formatter options
      --         black = { enabled = true },
      --         autopep8 = { enabled = false },
      --         yapf = { enabled = false },
      --         -- linter options
      --         pylint = { enabled = true, executable = "pylint" },
      --         pyflakes = { enabled = false },
      --         pycodestyle = { enabled = false },
      --         -- type checker
      --         pylsp_mypy = { enabled = true },
      --         -- auto-completion options
      --         jedi_completion = { fuzzy = true },
      --         -- import sorting
      --         pyls_isort = { enabled = true },
      --       },
      --     },
      --   },
      -- },
      pyright = {}, -- npm install -g pyright
      clangd = {
        cmd = {
          "clangd",
          "--background-index",
          "-j=12",
          "--query-driver=/usr/bin/**/clang-*,/bin/clang,/bin/clang++,/usr/bin/gcc,/usr/bin/g++",
          "--clang-tidy",
          "--clang-tidy-checks=*",
          "--all-scopes-completion",
          "--cross-file-rename",
          "--completion-style=detailed",
          "--header-insertion-decorators",
          "--header-insertion=iwyu",
          "--pch-storage=memory",
        },
      },
      vls = { -- v ls --install
        cmd = { "v", "ls" },
        filetypes = { "v", "vlang", "vv" },
        root_dir = require("lspconfig/util").root_pattern { "v.mod", ".git" },
      }, -- vlang
      v_analyzer = { -- official recommend
        cmd = { "v-analyzer" },
        filetypes = { "v", "vsh", "vv" },
        root_dir = require("lspconfig/util").root_pattern { "v.mod", ".git" },
      },
      cmake = {},
      bashls = {},
      jdtls = {},
      dockerls = {},
      gopls = {
        cmd = { "gopls" },
        filetypes = { "go", "gomod", "gowork", "gotmpl" },
        root_dir = require("lspconfig/util").root_pattern { "go.work", "go.mod", ".git" },
      },
      emmet_ls = {
        filetypes = {
          "html",
          "typescriptreact",
          "javascriptreact",
          "css",
          "sass",
          "scss",
          "less",
          "svelte",
        },
      },
      html = {},
      svelte = {},
      denols = {
        filetypes = { "typescript", "typescriptreact", "typescript.tsx" },
        root_dir = require("lspconfig/util").root_pattern { "deno.json", "deno.jsonc" },
      },
      jsonls = {
        filetypes = { "json", "jsonc" },
        settings = {
          json = {
            schemas = require("schemastore").json.schemas(),
          },
        },
        setup = {
          commands = {
            Format = {
              function()
                vim.lsp.buf.range_formatting({}, { 0, 0 }, { vim.fn.line "$", 0 })
              end,
            },
          },
        },
      },
      cssls = {},
      volar = {},
      vimls = {},
      unocss = {},
      asm_lsp = {},
      zls = {},
      tailwindcss = {},
    }

    -- +++++++++++++++++++++++node++++++++++++++++++++++++++++++++++++
    local node_bin = "/usr/local/bin/node"
    vim.g.node_host_prog = node_bin
    -- for mason.nvim
    -- prereq - install lsp server in that node/bin npm i -g typescript-language-server
    -- (handled by :Mason currently)
    vim.cmd("let $PATH = '" .. node_bin .. ":' . $PATH")
    -- +++++++++++++++++++++++node++++++++++++++++++++++++++++++++++++

    local default_config = {
      capabilities = capabilities,
      flags = {
        debounce_text_changes = 150,
      },
      inlay_hints = { enabled = true },
    }

    local lspconfig = require "lspconfig"
    local setup_server = function(server_s)
      -- :help lspconfig-all.
      for server_name, value in pairs(server_s) do
        local server_config = vim.tbl_deep_extend("keep", default_config, value)
        lspconfig[server_name].setup(server_config)

        -- -- 配置LSP服务
        -- if server_name == "vls" then
        --   lspconfig.vls.setup {}
        --   vim.cmd([[au BufNewFile,BufRead *.v set filetype=vlang]])
        -- elseif server_name == "v_analyzer" then
        --   lspconfig.v_analyzer.setup(server_config)
        -- else
        --   lspconfig[server_name].setup(server_config)
        -- end
      end
    end

    -- After setting up mason-lspconfig you may set up servers via lspconfig
    setup_server(servers)

    -- 单独设置
    -- lspconfig.rust_analyzer.setup {}

    local hover_highlight_word = function(client, buffer)
      -- https://neovim.io/doc/user/lsp.html
      -- vim.notify(client.name, vim.log.levels.INFO, {})
      if client.server_capabilities.documentHighlightProvider then
        -- 光标所在的字符词语高亮相同的单词， 可以用插件： "RRethy/vim-illuminate",
        -- vim.api.nvim_exec(
        --   [[
        --     augroup lsp_document_highlight
        --       autocmd! * <buffer>
        --       autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight()
        --       autocmd CursorMoved <buffer> lua vim.lsp.buf.clear_references()
        --     augroup END
        --   ]],
        --   false
        -- )
        vim.api.nvim_create_autocmd({ "CursorHold", "CursorHoldI" }, {
          buffer = buffer,
          callback = vim.lsp.buf.document_highlight,
        })
        vim.api.nvim_create_autocmd({ "CursorMoved", "CursorMovedI" }, {
          buffer = buffer,
          callback = vim.lsp.buf.clear_references,
        })
      end
    end

    local clone = function(opts1, opts2)
      return vim.tbl_deep_extend("force", opts1, opts2)
    end
    local formatting = function(client, buffer)
      local bufopts = {
        noremap = true,
        silent = true,
        buffer = buffer,
        desc = "xxx",
      }

      local fmt = function()
        require("conform").format {
          bufnr = buffer,
          async = false,
          lsp_fallback = true, -- conform.nvim 如果不支持就用 Lsp 的格式化
          timeout_ms = 500,
        }
      end

      -- 插件 conform.nvim 格式化
      vim.keymap.set({ "n", "v" }, "<leader>fc", function()
        fmt()
      end, clone(bufopts, { desc = "conform.nvim: [F]ormat code" }))

      if client.server_capabilities.documentFormattingProvider then
        -- 快捷键格式化
        -- vim.keymap.set({ "n", "v" }, "<leader>ls", function()
        --   --  注意这里 async = true
        --   -- vim.lsp.buf.format { async = true }
        --   fmt()
        -- end, clone(bufopts, { desc = "LSP: [F]ormat code" }))

        -- 保存格式化
        vim.api.nvim_create_autocmd("BufWritePre", {
          group = vim.api.nvim_create_augroup("LspFormatting", { clear = true }),
          buffer = bufopts.buffer,
          callback = function()
            -- 注意这里 async = false,否则会不断有问题
            -- vim.lsp.buf.format { async = false }
            fmt()
          end,
        })
      end
    end

    local on_attach = function(client, buffer)
      -- vim.notify("Server Name: " .. client.name)

      vim.api.nvim_buf_set_option(buffer, "omnifunc", "v:lua.vim.lsp.omnifunc")

      local nmap = function(keys, func, desc)
        -- See `:help vim.lsp.*` for documentation on any of the below functions
        if desc then
          desc = "LSP: " .. desc
        end

        local bufopts = {
          buffer = buffer,
          desc = desc,
          noremap = true,
          silent = true,
        }
        vim.keymap.set("n", keys, func, bufopts)
      end

      -- nmap("gD", vim.lsp.buf.declaration, "[G]oto [D]eclaration")
      -- nmap("K", vim.lsp.buf.hover, "[H]over [D]ocumentation")
      -- nmap("gd", vim.lsp.buf.definition, "[G]oto [D]efinition")
      -- nmap("gi", vim.lsp.buf.implementation, "[G]oto [I]mplementation")
      -- nmap("<leader>D", vim.lsp.buf.type_definition, "Type [D]efinition")
      -- 跳转到实现
      -- nmap("gD", vim.lsp.buf.implementation, "[G]oto [I]mplementation")

      -- 跳转到定义
      nmap("gd", "<cmd>Lspsaga goto_definition<CR>", "Goto definition")
      -- 查找使用的地方
      nmap("gs", "<cmd>Lspsaga lsp_finder<CR>")
      -- 跳转到类型声明位置
      nmap("gp", "<cmd>Lspsaga goto_type_definition<CR>", "Goto type definition")
      -- 跳转到引用位置
      nmap("gr", require("telescope.builtin").lsp_references, "[G]oto [R]eferences")

      -- peek
      nmap("<leader>gd", "<cmd>Lspsaga peek_definition<CR>", "Peek definition")
      nmap("<leader>pd", "<cmd>Lspsaga peek_type_definition<CR>", "Peek type definition")
      nmap("<leader>ds", require("telescope.builtin").lsp_document_symbols, "[D]ocument [S]ymbols")
      nmap("<leader>ws", require("telescope.builtin").lsp_dynamic_workspace_symbols, "[W]orkspace [S]ymbols")

      -- rename  close: <C-k>
      nmap("<leader>r", "<cmd>Lspsaga rename<CR>")

      nmap("K", ":Lspsaga hover_doc<cr>", "Hover Documentation")
      -- nmap("K", ":Lspsaga hover_doc ++keep<cr>", "Hover Documentation") -- 保持在右上角
      -- nmap('K', vim.lsp.buf.hover, 'Hover Documentation')
      nmap("<C-k>", vim.lsp.buf.signature_help, "Signature Documentation")

      nmap("<space>ca", ":Lspsaga code_action<cr>", "Code action")
      nmap("gh", ":Lspsaga finder<cr>", "Search and preview implementation of interfaces") --  显示引用和实现的

      nmap("[e", "<cmd>Lspsaga diagnostic_jump_prev<CR>", "Diagnostic jump prev")
      nmap("]e", "<cmd>Lspsaga diagnostic_jump_next<CR>", "Diagnostic jump next")
      nmap("<leader>sb", "<cmd>Lspsaga show_buf_diagnostics<CR>", "Show buf diagnostics")
      nmap("<leader>sw", "<cmd>Lspsaga show_workspace_diagnostics<CR>", "Show workspace diagnostics")
      nmap("<leader>sl", "<cmd>Lspsaga show_line_diagnostics<CR>", "Show line diagnostics")
      nmap("<leader>sc", "<cmd>Lspsaga show_cursor_diagnostics<CR>", "Show cursor diagnostics")

      -- File Map    e 选择  q 关闭  o 折叠
      nmap("<space>ol", ":Lspsaga outline<cr>", "Open outline Map") -- 右侧 Map 文件导航地图
    end

    vim.api.nvim_create_autocmd("LspAttach", {
      group = vim.api.nvim_create_augroup("UserLspConfig", {}),
      callback = function(ev)
        -- 获取服务信息
        local client = vim.lsp.get_client_by_id(ev.data.client_id)

        -- 高亮鼠标悬浮的相关的所有词语, 注意代码位置顺序
        hover_highlight_word(client, ev.buf)

        if client ~= nil and client.supports_method "textDocument/formatting" then
          -- 调用格式化的, 相关配置
          formatting(client, ev.buf)
        else
          vim.notify "Not supports formatting..."
        end

        -- 光标所在
        -- local group = vim.api.nvim_create_augroup("ShowDiagnosticsOnHover", { clear = false })
        -- vim.api.nvim_create_autocmd({ "CursorHold", "CursorHoldI" }, {
        --   buffer = ev.buf,
        --   command = "Lspsaga show_line_diagnostics",
        --   group = group,
        -- })

        if client ~= nil and client.name == "svelte" then
          vim.api.nvim_create_autocmd("BufWritePost", {
            pattern = { "*.js", "*.ts" },
            callback = function(ctx)
              -- Here use ctx.match instead of ctx.file
              client.notify("$/onDidChangeTsOfJsFile", { uri = ctx.match })
            end,
          })
        end

        on_attach(client, ev.buf)

        -- if vim.lsp.buf.inlay_hint then
        --   if client ~= nil and client.server_capabilities.inlayHintProvider then
        --     if vim.lsp.buf.inlay_hint then
        --       vim.keymap.set("n", "<leader>uh", function()
        --         vim.lsp.buf.inlay_hint(0, nil)
        --       end, { desc = "Toggle Inlay Hints" })
        --     end
        --
        --     vim.lsp.buf.inlay_hint(ev.buf, true)
        --   end
        -- end

        if client ~= nil and client.server_capabilities.inlayHintProvider then
          if vim.lsp.inlay_hint then
            -- vim.lsp.inlay_hint.enable(ev.buf, true)
            vim.keymap.set("n", "<space>hc", function()
              vim.lsp.inlay_hint.enable(not vim.lsp.inlay_hint.is_enabled())
            end, { desc = "Toggle Inlay Hints." })
          end
        end
      end,
    })
  end,
}

return M
